package com.gmrz.uaf.policy.verification;
 import com.gmrz.uaf.protocol.v1.processor.exception.UAFErrorCode;
 import com.gmrz.uaf.protocol.v1.schema.AuthenticationAssertion;
 import com.gmrz.uaf.protocol.v1.schema.AuthenticatorSpec;
 import com.gmrz.uaf.protocol.v1.schema.MatchCriteria;
 import com.gmrz.uaf.protocol.v1.schema.Policy;

 import java.util.ArrayList;
 import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
 import org.apache.logging.log4j.LogManager;
 import org.apache.logging.log4j.Logger;
 
 public class AuthenticationPolicyVerifier
   implements IPolicyVerifier<AuthenticationAssertion>
 {
   private static final Logger LOG = LogManager.getLogger(AuthenticationPolicyVerifier.class);
 
   public void filterDisallowedAssertions(List<AuthenticationAssertion> assertions, Map<String, AuthenticatorSpec> m, Policy policy)
     throws PolicyVerificationException
   {
     try
     {
       LOG.debug(" Filter disallowed assertions by using authentication policy disallowed MatchCriteria ");
 
       PolicyVerificationUtil.verifyInputs(assertions, m, policy);
       Iterator i$;
       MatchCriteria dc;
       Iterator authenticationAssertionIter;
       if ((policy.getDisallowed() != null) && (policy.getDisallowed().size() > 0))
       {
         List disallowedCriteria = policy.getDisallowed();
         for (i$ = disallowedCriteria.iterator(); i$.hasNext(); ) {
           dc = (MatchCriteria)i$.next();
           for (authenticationAssertionIter = assertions.iterator(); authenticationAssertionIter.hasNext(); )
           {
             AuthenticationAssertion assertion = (AuthenticationAssertion)authenticationAssertionIter.next();
 
             AuthenticatorSpec spec = PolicyVerificationUtil.getAuthenticatorSpecForAAID(assertion.getSignResponse().getSignData().getAAID(), m);
 
             if (Matcher.compare(dc, spec))
             {
               LOG.info(" Assertion matches the disallowed MatchCriteria, removing the assertion from the list with AAID : {}", spec.getAAID());
 
               authenticationAssertionIter.remove();
             }
           } }
       }
       else {
         LOG.debug("There is no disallowed match criteria in the authentication policy : {}", policy.getPolicyName());
       }
       LOG.debug("Completed filtering disallowed assertions.");
     }
     catch (PolicyVerificationException pve)
     {
       LOG.error("Error occurred during processing disallowed matchCriteria for Authentication policy Verification : {}", pve.getMessage());
       throw pve;
     }
     catch (Exception e) {
       LOG.error("Following Error occurred during Authentication policy Verification : {}", e.getMessage());
       throw new PolicyVerificationException(UAFErrorCode.SYS_POLICY_VERIFICATION_FAILED, "Error occurred during Authentication policy Verification");
     }
   }
 
   public List<AuthenticationAssertion> selectAcceptedAssertions(List<AuthenticationAssertion> assertions, Map<String, AuthenticatorSpec> m, Policy policy)
     throws PolicyVerificationException
   {
     LOG.debug(" Select the assertions by using authentication policy accepted MatchCriteria ");
 
     PolicyVerificationUtil.verifyInputs(assertions, m, policy);
 
     List acceptedMatchCriteria = policy.getAccepted();
 
     boolean matchSuccessful = false;
 
     List assertionsUsed = new ArrayList();
 
     for (Iterator acceptedMatchCriteriaIndexIterator = acceptedMatchCriteria.iterator(); acceptedMatchCriteriaIndexIterator.hasNext(); )
     {
       List<MatchCriteria> acceptedMatchCriteriaIndex = (List)acceptedMatchCriteriaIndexIterator.next();
 
       for (MatchCriteria matchCriteria : acceptedMatchCriteriaIndex) {
         matchSuccessful = false;
         for (AuthenticationAssertion assertion : assertions)
         {
           AuthenticatorSpec spec = PolicyVerificationUtil.getAuthenticatorSpecForAAID(assertion.getSignResponse().getSignData().getAAID(), m);
 
           if (isMatch(assertion, spec, matchCriteria))
           {
             matchSuccessful = true;
             if (assertionsUsed.contains(assertion)) break;
             assertionsUsed.add(assertion); break;
           }
 
         }
 
         if (!matchSuccessful)
         {
           assertionsUsed.clear();
 
           break;
         }
 
       }
 
       if (matchSuccessful == true)
       {
         break;
       }
     }
     if ((matchSuccessful == true) && (assertionsUsed.size() > 0))
       LOG.info("Authentication Policy Verification successful");
     else {
       throw new PolicyVerificationException(UAFErrorCode.SYS_POLICY_VERIFICATION_FAILED,
               "Failed in verifying policy for authentication");
     }
 
     return assertionsUsed;
   }
 
   private boolean validateAssertionTagsForAuthentication(AuthenticationAssertion assertion, AuthenticatorSpec spec, MatchCriteria matchCriteria)
     throws PolicyVerificationException
   {
     String assertionAAID = assertion.getSignResponse().getSignData().getAAID();
     int authenticatorVersionInAssertion = assertion.getSignResponse().getSignData().getAuthenticatorVersion();
 
     boolean checkVersion = PolicyVerificationUtil.checkAuthenticatorVersion(matchCriteria, assertionAAID, authenticatorVersionInAssertion);
     if (!checkVersion) {
       LOG.debug("Validation of the assertion against the authenticator spec failed for authentication version(in assertion) : {} ", Integer.valueOf(authenticatorVersionInAssertion));
       return false;
     }
 
     if (spec.getAlgorithm() != assertion.getSignResponse().getSignData().getSignAlgorithm()) {
       LOG.debug("Validation of the assertion against the authenticator spec failed for authentication algo(in assertion) : {}", Integer.valueOf(assertion.getSignResponse().getSignData().getSignAlgorithm()));
       return false;
     }
     if (!spec.getAssertionScheme().equalsIgnoreCase(assertion.getSignDataScheme())) {
       LOG.debug("Validation of the assertion against the authenticator spec failed for supported scheme(in assertion) : {}", assertion.getSignDataScheme());
       return false;
     }
 
     LOG.debug(" Completed policy validation of the assertions.");
 
     return true;
   }
 
   private boolean isMatch(AuthenticationAssertion assertion, AuthenticatorSpec spec, MatchCriteria matchCriteria) throws PolicyVerificationException
   {
     LOG.debug("check if the assertion macthes the Policy MatchCriteria");
     if (Matcher.compare(matchCriteria, spec)) {
       LOG.debug("The assertion matches the authenticator metadata, check assertion tags.");
 
       boolean isValidAssertion = validateAssertionTagsForAuthentication(assertion, spec, matchCriteria);
       if (isValidAssertion == true) {
         LOG.debug("The authentication assertion matches policy MatchCriteria.");
         return true;
       }
     }
 
     LOG.debug("The authentication assertion does NOT match policy MatchCriteria.");
     return false;
   }
 }
